package com.zqy.aop;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.util.Map;

@Aspect
@Component
public class ParamInfoLog {

    protected final static Logger logger = (Logger)LoggerFactory.getLogger(ParamInfoLog.class);

    @Pointcut("execution(* com.zqy.controller..*(..))")
    public void controller() {}

    @Pointcut("execution(* com.zqy.service..*(..))")
    public void service() {}

    @Pointcut("execution(* com.zqy.dao..*(..))")
    public void repository() {}

    @Around("controller()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        long startTimeMillis = System.currentTimeMillis();
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes)ra;
        if (sra == null) {
            return pjp.proceed();
        }
        HttpServletRequest request = sra.getRequest();
        String requestMethod = request.getMethod();
        Map<String, String[]> inputParamMap = request.getParameterMap();
        String body = getBody(pjp);
        String requestPath = request.getRequestURI();
        logger.info("\n-------------------------------------\n" + "RequestUrl:" + requestPath + "\n" + "requestMethod:"
            + requestMethod + "\n" + "RequestParams:" + JSON.toJSON(inputParamMap) + "\n" + "RequestBody:" + body
            + "\n-------------------------------------\n");
        Object result = pjp.proceed();
        long endTimeMillis = System.currentTimeMillis();
        logger.info("\n" + "Begin:" + "\n" + "Url:" + requestPath + "\n" + "requestMethod:" + requestMethod + "\n"
            + "RequestParams:" + JSON.toJSON(inputParamMap) + "\n" + "RequestBody:" + body + "\n" + "ResponseResults:"
            + JSON.toJSON(result) + "\n" + "Cost:" + (endTimeMillis - startTimeMillis) + " ms " + "\n" + "End" + "\n");
        return result;
    }

    // @Before("controller()")
    // public void controller(JoinPoint point) {
    // MethodSignature signature = (MethodSignature) point.getSignature();
    // int count = signature.getMethod().getDeclaredAnnotations().length;
    // String requestPath = count >= 1 ?
    // signature.getMethod().getAnnotation(RequestMapping.class).value()[0] :
    // "";
    //
    // String info = String.format("path:%s | %s", requestPath,
    // getMethodInfo(point));
    // log.info(info);
    // }

    private String getBody(ProceedingJoinPoint jp) {
        MethodSignature methodSignature = (MethodSignature)jp.getSignature();
        Annotation[][] annotations = methodSignature.getMethod().getParameterAnnotations();
        Object[] objects = jp.getArgs();
        int index = 0;
        boolean headerParamFlag = Boolean.FALSE;
        LOOP:
        for (int i = 0; i < annotations.length; i++) {
            for (Annotation a : annotations[i]) {
                if (a.annotationType() == RequestBody.class) {
                    index = i;
                    headerParamFlag = Boolean.TRUE;
                    break LOOP;
                }
            }
        }
        String body = "{}";
        if (headerParamFlag) {
            Object obj = objects[index];
            try {
                body = JSON.toJSONString(obj);
            } catch (JSONException e) {
                body = "requestBody can't change to json";
            }
        }
        return body;
    }

    @Before("service()")
    public void service(JoinPoint point) {
        logger.info(String.format("%s", getMethodInfo(point)));
    }

    @Before("repository()")
    public void repository(JoinPoint point) {
        logger.info(String.format("%s", getMethodInfo(point)));
    }

    private String getMethodInfo(JoinPoint point) {
        String className = point.getSignature().getDeclaringType().getSimpleName();
        String methodName = point.getSignature().getName();
        String[] parameterNames = ((MethodSignature)point.getSignature()).getParameterNames();
        StringBuilder sb = null;
        if (parameterNames != null && parameterNames.length > 0) {
            sb = new StringBuilder();
            for (int i = 0; i < parameterNames.length; i++) {
                String value = point.getArgs()[i] != null ? point.getArgs()[i].toString() : "null";
                sb.append(parameterNames[i] + ":" + value + "; ");
            }
        }
        sb = sb == null ? new StringBuilder() : sb;
        String info = String.format("class:%s | method:%s | args:%s", className, methodName, sb.toString());
        return info;
    }
}
